home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2005 October / PCWOCT05.iso / Software / FromTheMag / XAMPP 1.4.14 / xampp-win32-1.4.14-installer.exe / xampp / php / pear / HTML / Menu.php < prev    next >
PHP Script  |  2004-03-24  |  16KB  |  544 lines

  1. <?php
  2. //
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4                                                        |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2003 The PHP Group                                |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.0 of the PHP license,       |
  9. // | that is bundled with this package in the file LICENSE, and is        |
  10. // | available at through the world-wide-web at                           |
  11. // | http://www.php.net/license/2_02.txt.                                 |
  12. // | If you did not receive a copy of the PHP license and are unable to   |
  13. // | obtain it through the world-wide-web, please send a note to          |
  14. // | license@php.net so we can mail you a copy immediately.               |
  15. // +----------------------------------------------------------------------+
  16. // | Authors: Ulf Wendel <ulf.wendel@phpdoc.de>                           |
  17. // |          Sebastian Bergmann <sb@sebastian-bergmann.de>               |
  18. // |          Alexey Borzov <avb@php.net>                                 |
  19. // +----------------------------------------------------------------------+
  20. //
  21. // $Id: Menu.php,v 1.10 2004/01/18 17:35:00 avb Exp $
  22. //
  23.  
  24. // Types of the menu entries, instead of former magic numbers
  25. define('HTML_MENU_ENTRY_INACTIVE',      0);
  26. define('HTML_MENU_ENTRY_ACTIVE',        1);
  27. define('HTML_MENU_ENTRY_ACTIVEPATH',    2);
  28. define('HTML_MENU_ENTRY_PREVIOUS',      3);
  29. define('HTML_MENU_ENTRY_NEXT',          4);
  30. define('HTML_MENU_ENTRY_UPPER',         5);
  31. define('HTML_MENU_ENTRY_BREADCRUMB',    6); // like activepath, but for 'urhere' type
  32.  
  33. /**
  34. * Generates a HTML menu from a multidimensional hash.
  35. *
  36. * Special thanks to the original author: Alex Vorobiev  <sasha@mathforum.com>.
  37. *
  38. * @version  $Revision: 1.10 $
  39. * @author   Ulf Wendel <ulf.wendel@phpdoc.de>
  40. * @author   Alexey Borzov <avb@php.net>
  41. * @access   public
  42. * @package  HTML_Menu
  43. */
  44. class HTML_Menu 
  45. {
  46.    /**
  47.     * Menu structure as a multidimensional hash.
  48.     * @var  array
  49.     * @see  setMenu(), Menu()
  50.     */
  51.     var $_menu = array();
  52.  
  53.    /**
  54.     * Mapping from URL to menu path.
  55.     * @var  array
  56.     * @see  getPath()
  57.     */
  58.     var $_urlMap = array();
  59.  
  60.    /**
  61.     * Path to the current menu item.
  62.     * @var  array
  63.     * @see  get(), getPath()
  64.     */
  65.     var $_path = array();
  66.  
  67.    /**
  68.     * Menu type: tree, rows, you-are-here.
  69.     * @var  array
  70.     * @see  setMenuType()
  71.     */
  72.     var $_menuType = 'tree';
  73.  
  74.    /**
  75.     * URL Environment Variable
  76.     * @var  string
  77.     */
  78.     var $_urlEnvVar = 'PHP_SELF';
  79.  
  80.    /**
  81.     * The URL to use an URL for the current page, instead of the one normally
  82.     * taken from env. variables
  83.     * @var string
  84.     * @see forceCurrentUrl(), getCurrentUrl()
  85.     */
  86.     var $_forcedUrl = '';
  87.  
  88.    /**
  89.     * URL of the current page.
  90.     * @see  getCurrentURL(), getPath()
  91.     */
  92.     var $_currentUrl = '';
  93.  
  94.    /**
  95.     * The renderer being used to output the menu
  96.     * @var object HTML_Menu_Renderer
  97.     * @see render()
  98.     */
  99.     var $_renderer = null;
  100.  
  101.    /**
  102.     * Prefix for menu URLs 
  103.     * @var string
  104.     * @see setUrlPrefix()
  105.     */
  106.     var $_urlPrefix = '';
  107.  
  108.    /**
  109.     * Initializes the menu, sets the type and menu structure.
  110.     *
  111.     * @param    array   menu structure
  112.     * @param    string  menu type
  113.     * @param    string  env. variable used to determine current URL
  114.     * @see      setMenuType(), setMenu(), setURLEnvVar()
  115.     */
  116.     function HTML_Menu($menu = null, $type = 'tree', $urlEnvVar = 'PHP_SELF') 
  117.     {
  118.         if (is_array($menu)) {
  119.             $this->setMenu($menu);
  120.         }
  121.         $this->setMenuType($type);
  122.         $this->setURLEnvVar($urlEnvVar);
  123.     }
  124.  
  125.  
  126.    /**
  127.     * Sets the menu structure.
  128.     *
  129.     * The menu structure is defined by a multidimensional hash. This is
  130.     * quite "dirty" but simple and easy to traverse. An example
  131.     * show the structure. To get the following menu:
  132.     *
  133.     * 1  - Projects
  134.     * 11 - Projects => PHPDoc
  135.     * 12 - Projects => Forms
  136.     * 2  - Stuff
  137.     *
  138.     * you need the array:
  139.     *
  140.     * $menu = array(
  141.     *           1 => array(
  142.     *                  'title' => 'Projects',
  143.     *                  'url' => '/projects/index.php',
  144.     *                  'sub' => array(
  145.     *                           11 => array(
  146.     *                                       'title' => 'PHPDoc',
  147.     *                                       ...
  148.     *                                     ),
  149.     *                           12 => array( ... ),
  150.     *                 )
  151.     *             ),
  152.     *           2 => array( 'title' => 'Stuff', 'url' => '/stuff/index.php' )
  153.     *        )
  154.     *
  155.     * Note the index 'sub' and the nesting. Note also that 1, 11, 12, 2
  156.     * must be unique. The class uses them as ID's.
  157.     *
  158.     * @param    array
  159.     * @access   public
  160.     */
  161.     function setMenu($menu) 
  162.     {
  163.         $this->_menu   = $menu;
  164.         $this->_urlMap = array();
  165.     }
  166.  
  167.  
  168.    /**
  169.     * Sets the type of the menu.
  170.     * 
  171.     * Available types are: 'tree', 'rows', 'urhere', 'prevnext', 'sitemap'.
  172.     *
  173.     * @param    string type name
  174.     * @access   public
  175.     */
  176.     function setMenuType($menuType) 
  177.     {
  178.         $menuType = strtolower($menuType);
  179.         if (in_array($menuType, array('tree', 'rows', 'urhere', 'prevnext', 'sitemap'))) {
  180.             $this->_menuType = $menuType;
  181.         } else {
  182.             $this->_menuType = 'tree';
  183.         }
  184.     }
  185.  
  186.  
  187.    /**
  188.     * Sets the environment variable to use to get the current URL.
  189.     *
  190.     * @param    string  environment variable for current URL
  191.     * @access   public
  192.     */
  193.     function setURLEnvVar($urlEnvVar) 
  194.     {
  195.         $this->_urlEnvVar = $urlEnvVar;
  196.     }
  197.  
  198.  
  199.    /**
  200.     * Returns the HTML menu.
  201.     *
  202.     * @param    string  Menu type: tree, urhere, rows, prevnext, sitemap
  203.     * @return   string  HTML of the menu
  204.     * @access   public
  205.     * @see render()
  206.     */
  207.     function get($menuType = '') 
  208.     {
  209.         include_once 'HTML/Menu/DirectRenderer.php';
  210.         $renderer =& new HTML_Menu_DirectRenderer();
  211.         $this->render($renderer, $menuType);
  212.         return $renderer->toHtml();
  213.     }
  214.  
  215.  
  216.    /**
  217.     * Prints the HTML menu.
  218.     *
  219.     * @access   public
  220.     * @param    string  Menu type: tree, urhere, rows, prevnext, sitemap
  221.     * @see      get(), render()
  222.     */
  223.     function show($menuType = '') 
  224.     {
  225.         print $this->get($menuType);
  226.     }
  227.  
  228.  
  229.    /**
  230.     * Renders the menu.
  231.     *
  232.     * @access public
  233.     * @param  object HTML_Menu_Renderer  Renderer to use
  234.     * @param  string    type of the menu
  235.     * @throws PEAR_Error
  236.     */
  237.     function render(&$renderer, $menuType = '')
  238.     {
  239.         if ('' != $menuType) {
  240.             $this->setMenuType($menuType);
  241.         }
  242.         $this->_renderer =& $renderer;
  243.         // the renderer will throw an error if it is unable to process this menu type
  244.         $res = $this->_renderer->setMenuType($this->_menuType);
  245.         if (is_object($res) && is_a($res, 'PEAR_Error')) {
  246.             return $res;
  247.         }
  248.  
  249.         // storing to a class variable saves some recursion overhead
  250.         $this->_path = $this->getPath();
  251.  
  252.         switch ($this->_menuType) {
  253.             case 'rows': 
  254.                 $this->_renderRows($this->_menu);
  255.                 break;
  256.  
  257.             case 'prevnext': 
  258.                 $this->_renderPrevNext($this->_menu);
  259.                 break;
  260.  
  261.             case 'urhere':
  262.                 $this->_renderURHere($this->_menu);
  263.                 break;
  264.  
  265.             default:
  266.                 $this->_renderTree($this->_menu);
  267.         } // switch
  268.     }
  269.  
  270.  
  271.    /**
  272.     * Finds the type for the node.
  273.     * 
  274.     * @access private
  275.     * @param mixed   Node id
  276.     * @param string  Node 'url' attribute
  277.     * @param int     Level in the tree
  278.     * @return int    Node type (one of HTML_MENU_ENTRY_* constants)
  279.     */
  280.     function _findNodeType($nodeId, &$nodeUrl, $level)
  281.     {
  282.         $nodeUrl = $this->_urlPrefix . ((empty($this->_urlPrefix) || '/' != $nodeUrl{0})? $nodeUrl: substr($nodeUrl, 1));
  283.         if ($this->_currentUrl == $nodeUrl) {
  284.             // menu item that fits to this url - 'active' menu item
  285.             return HTML_MENU_ENTRY_ACTIVE;
  286.         } elseif (isset($this->_path[$level]) && $this->_path[$level] == $nodeId) {
  287.             // processed menu item is part of the path to the active menu item
  288.             return 'urhere' == $this->_menuType? HTML_MENU_ENTRY_BREADCRUMB: HTML_MENU_ENTRY_ACTIVEPATH;
  289.         } else {
  290.             // not selected, not a part of the path to the active menu item
  291.             return HTML_MENU_ENTRY_INACTIVE;
  292.         }
  293.     }
  294.  
  295.  
  296.    /**
  297.     * Renders the tree menu ('tree' and 'sitemap')
  298.     * 
  299.     * @access private
  300.     * @param  array     (sub)menu being rendered
  301.     * @param  int       current depth in the tree structure
  302.     */
  303.     function _renderTree($menu, $level = 0)
  304.     {
  305.         foreach ($menu as $node_id => $node) {
  306.             $type = $this->_findNodeType($node_id, $node['url'], $level);
  307.  
  308.             $this->_renderer->renderEntry($node, $level, $type);
  309.             $this->_renderer->finishRow($level);
  310.  
  311.             // follow the subtree if the active menu item is in it or if we want the full menu
  312.             if (('sitemap' == $this->_menuType || HTML_MENU_ENTRY_INACTIVE != $type) && isset($node['sub'])) {
  313.                 $this->_renderTree($node['sub'], $level + 1);
  314.             }
  315.         }
  316.         $this->_renderer->finishLevel($level);
  317.         if (0 == $level) {
  318.             $this->_renderer->finishMenu($level);
  319.         }
  320.     }
  321.  
  322.  
  323.    /**
  324.     * Renders the 'urhere' menu
  325.     * 
  326.     * @access private
  327.     * @param  array     (sub)menu being rendered
  328.     * @param  int       current depth in the tree structure
  329.     */
  330.     function _renderURHere($menu, $level = 0)
  331.     {
  332.         foreach ($menu as $node_id => $node) {
  333.             $type = $this->_findNodeType($node_id, $node['url'], $level);
  334.  
  335.             if (HTML_MENU_ENTRY_INACTIVE != $type) {
  336.                 $this->_renderer->renderEntry($node, $level, $type);
  337.                 // follow the subtree if the active menu item is in it
  338.                 if (isset($node['sub'])) {
  339.                     $this->_renderURHere($node['sub'], $level + 1);
  340.                 }
  341.             }
  342.         }
  343.         if (0 == $level) {
  344.             $this->_renderer->finishRow($level);
  345.             $this->_renderer->finishMenu($level);
  346.         }
  347.     }
  348.  
  349.  
  350.    /**
  351.     * Renders the 'rows' menu
  352.     * 
  353.     * @access private
  354.     * @param  array     (sub)menu being rendered
  355.     * @param  int       current depth in the tree structure
  356.     */
  357.     function _renderRows($menu, $level = 0)
  358.     {
  359.         $submenu = false;
  360.  
  361.         foreach ($menu as $node_id => $node) {
  362.             $type = $this->_findNodeType($node_id, $node['url'], $level);
  363.  
  364.             $this->_renderer->renderEntry($node, $level, $type);
  365.  
  366.             // follow the subtree if the active menu item is in it
  367.             if (HTML_MENU_ENTRY_INACTIVE != $type && isset($node['sub'])) {
  368.                 $submenu = $node['sub'];
  369.             }
  370.         }
  371.  
  372.         // every (sub)menu has its own table
  373.         $this->_renderer->finishRow($level);
  374.         $this->_renderer->finishMenu($level);
  375.  
  376.         // go deeper if neccessary
  377.         if ($submenu) {
  378.             $this->_renderRows($submenu, $level + 1);
  379.         }
  380.     }
  381.  
  382.  
  383.    /**
  384.     * Renders the 'prevnext' menu
  385.     * 
  386.     * @access private
  387.     * @param  array     (sub)menu being rendered
  388.     * @param  int       current depth in the tree structure
  389.     * @param  int       flag indicating whether to finish processing
  390.     *                   (0 - continue, 1 - this is "next" node, 2 - stop)
  391.     */
  392.     function _renderPrevNext($menu, $level = 0, $flagStop = 0)
  393.     {
  394.         static $last_node = array(), $up_node = array();
  395.  
  396.         foreach ($menu as $node_id => $node) {
  397.             if (0 != $flagStop) {
  398.                 // add this item to the menu and stop recursion - (next >>) node
  399.                 if ($flagStop == 1) {
  400.                     $this->_renderer->renderEntry($node, $level, HTML_MENU_ENTRY_NEXT);
  401.                     $flagStop = 2;
  402.                 }
  403.                 break;
  404.  
  405.             } else {
  406.                 $type = $this->_findNodeType($node_id, $node['url'], $level);
  407.                 if (HTML_MENU_ENTRY_ACTIVE == $type) {
  408.                     $flagStop = 1;
  409.  
  410.                     // WARNING: if there's no previous take the first menu entry - you might not like this rule!
  411.                     if (0 == count($last_node)) {
  412.                         reset($this->_menu);
  413.                         list($node_id, $last_node) = each($this->_menu);
  414.                     }
  415.                     $this->_renderer->renderEntry($last_node, $level, HTML_MENU_ENTRY_PREVIOUS);
  416.  
  417.                     // WARNING: if there's no up take the first menu entry - you might not like this rule!
  418.                     if (0 == count($up_node)) {
  419.                         reset($this->_menu);
  420.                         list($node_id, $up_node) = each($this->_menu);
  421.                     }
  422.                     $this->_renderer->renderEntry($up_node, $level, HTML_MENU_ENTRY_UPPER);
  423.                 }
  424.             }
  425.  
  426.             // remember the last (<< prev) node
  427.             $last_node = $node;
  428.  
  429.             if (isset($node['sub'])) {
  430.                 if (HTML_MENU_ENTRY_INACTIVE != $type) {
  431.                     $up_node = $node;
  432.                 }
  433.                 $flagStop = $this->_renderPrevNext($node['sub'], $level + 1, $flagStop);
  434.             }
  435.         }
  436.  
  437.         if (0 == $level) {
  438.             $this->_renderer->finishRow($level);
  439.             $this->_renderer->finishMenu($level);
  440.         }
  441.         return $flagStop;
  442.     }
  443.  
  444.  
  445.    /**
  446.     * Returns the path of the current page in the menu 'tree'.
  447.     *
  448.     * @return   array    path to the selected menu item
  449.     * @see      _buildUrlMap(), $_urlMap
  450.     */
  451.     function getPath() 
  452.     {
  453.         $this->_currentUrl = $this->getCurrentURL();
  454.         $this->_buildUrlMap($this->_menu, array());
  455.  
  456.         // If there is no match for the current URL, try to come up with
  457.         // the best approximation by shortening the url
  458.         while ($this->_currentUrl && !isset($this->_urlMap[$this->_currentUrl])) {
  459.             $this->_currentUrl = substr($this->_currentUrl, 0, -1);
  460.         }
  461.  
  462.         return $this->_urlMap[$this->_currentUrl];
  463.     }
  464.  
  465.  
  466.    /**
  467.     * Builds the mappings from node url to the 'path' in the menu
  468.     *
  469.     * @access   private
  470.     * @param    array       (sub)menu being processed
  471.     * @param    array       path to the (sub)menu
  472.     * @return   boolean     true if the path to the current page was found, otherwise false.
  473.     * @see      getPath(), $_urlMap
  474.     */
  475.     function _buildUrlMap($menu, $path) 
  476.     {
  477.         foreach ($menu as $nodeId => $node) {
  478.             $url = $this->_urlPrefix . ((empty($this->_urlPrefix) || '/' != $node['url']{0})? $node['url']: substr($node['url'], 1));
  479.             $this->_urlMap[$url] = $path;
  480.  
  481.             if ($url == $this->_currentUrl) {
  482.                 return true;
  483.             }
  484.  
  485.             if (isset($node['sub']) && 
  486.                 $this->_buildUrlMap($node['sub'], array_merge($path, array($nodeId)))) {
  487.                 return true;
  488.             }
  489.         }
  490.         return false;
  491.     }
  492.  
  493.  
  494.    /**
  495.     * Returns the URL of the currently selected page.
  496.     *
  497.     * The returned string is used for all test against the URL's
  498.     * in the menu structure hash.
  499.     *
  500.     * @access public
  501.     * @return string
  502.     */
  503.     function getCurrentURL() 
  504.     {
  505.         if (!empty($this->_forcedUrl)) {
  506.             return $this->_forcedUrl;
  507.         } elseif (isset($_SERVER[$this->_urlEnvVar])) {
  508.             return $_SERVER[$this->_urlEnvVar];
  509.         } elseif (isset($GLOBALS[$this->_urlEnvVar])) {
  510.             return $GLOBALS[$this->_urlEnvVar];
  511.         } else {
  512.             return '';
  513.         }
  514.     }
  515.  
  516.  
  517.    /**
  518.     * Forces the given URL to be "current"
  519.     *
  520.     * @access public
  521.     * @param  string    Url to use
  522.     */
  523.     function forceCurrentUrl($url)
  524.     {
  525.         $this->_forcedUrl = $url;
  526.     }
  527.  
  528.  
  529.    /**
  530.     * Sets the prefix for the URLs in the menu
  531.     * 
  532.     * @param  string
  533.     * @access public
  534.     */
  535.     function setUrlPrefix($prefix)
  536.     {
  537.         if (('' != $prefix) && ('/' != substr($prefix, -1))) {
  538.             $prefix .= '/';
  539.         }
  540.         $this->_urlPrefix = $prefix;
  541.     }
  542. }
  543. ?>
  544.